/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.QName;
import com.netscenery.util.Unicode;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangKeyword;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.designer.MapInfo;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class Typedef extends Entity {
	private Type type;
	private String units;
	private String defaultValue;
	
	public Typedef(){
		
	}
	
	public Typedef(Env env,String name){
		this.setEnv(env);
		this.setName(name);
	}
	
	public Typedef(Env env,String name, Type type){
		
		this(env,name);
		this.type = type;
	}
	
	public Type createType(String builtinType,String derivedType){
		if (null == BuiltinType.getBuiltinType(builtinType)){
			return null;
		}
		Env subEnv = newSubEnv();
		if (null == derivedType){
			return Type.create(subEnv, builtinType);
		}
		Typedef type = null;
		
		QName qname = new QName(derivedType);
		type = Typedef.getDefByQName(env, qname);		
		if (null == type){
			return null;
		}
		
		if (!(type.getType().getBase().getFieldName().equals(builtinType))){
			return null;
		}
		
		return Type.create(subEnv, derivedType);
	}
	

	/**
	 * @return the type
	 */
	public Type getType() {
		return type;
	}

	/**
	 * @param type the type to set
	 */
	public void setType(Type type) {
		this.type = type;
	}

	/**
	 * @return the unit
	 */
	public String getUnits() {
		return units;
	}

	/**
	 * @param unit the unit to set
	 */
	public void setUnits(String units) {
		this.units = units;
	}

	/**
	 * @return the defaultValue
	 */
	public String getDefaultValue() {
		return defaultValue;
	}

	/**
	 * @param defaultValue the defaultValue to set
	 */
	public boolean setDefaultValue(String defaultValue) {
		if(null == defaultValue){
			this.defaultValue = null;
			return true;
		}
		if (!this.checkDefaultValue(defaultValue)){
			return false;
		}
		this.defaultValue = defaultValue;
		return true;
	}

	

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();
		if (isNullString(name) || !checkName(name)){
			validator.setIsValid(false);
			validator.addMsg(new Message(this, ErrTag.ERROR,ErrorCode.COMMON_INVALIDNAME));
		}
		
		if (null == this.type){
			validator.setIsValid(false);
			validator.addMsg(new Message(this, ErrTag.ERROR,"type is mandatory."));
		}
		else {
			Validator tValidator = type.validate();
			validator.copy(tValidator);
		}
		
		if (!isNullString(this.description)){
			if (Unicode.containChinese(this.description)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this,ErrTag.ERROR,"description",ErrorCode.INVALID_CHARACTER));
			}
		}
		
		if (!isNullString(this.reference)){
			if (Unicode.containChinese(this.reference)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this,ErrTag.ERROR,"reference",ErrorCode.INVALID_CHARACTER));
			}
		}	
		if (!isNullString(defaultValue)){
			if (getType().getBase() == BuiltinType.EMPTY){
				validator.setIsValid(false);
				validator.addMsg(new Message(this,ErrTag.ERROR,"empty type must have no default value."));
			}
			if (!checkDefaultValue(defaultValue)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this, ErrTag.ERROR,ErrorCode.INVALID_DEFAULTVALUE));
			}
		}
		Validator sValidator = super.validate();
		validator.copy(sValidator);
		return validator;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public YangStatement toYang() {
	
		YangStatement typedef = new YangStatement(YangStatement.YANG_STATEMENT_TYPEDEF);
		typedef.setArgument(this.name);
		
		if (null != this.getDescription()){
			YangStatement descr = new YangStatement(YangStatement.YANG_STATEMENT_DESCRIPTION,
					this.getDescription());
			typedef.addChild(descr);
		}
		
		if (null != this.getReference()){
			YangStatement ref = new YangStatement(YangStatement.YANG_STATEMENT_REFERENCE,
					this.getReference());
			typedef.addChild(ref);
		}

		if (!Status.CURRENT.equals(this.getStatus())) {
			YangStatement status = new YangStatement(
					YangStatement.YANG_STATEMENT_STATUS,
					this.getStatus().getStatus());
			typedef.addChild(status);
		}
		
		if (null != this.type){
			YangStatement type = this.type.writeYang();
			if (null != type){
				typedef.addChild(type);
			}
			
		}
		
		
		if (null != this.units){
			YangStatement units = new YangStatement(YangStatement.YANG_STATEMENT_UNITS,this.units);
			typedef.addChild(units);
		}
		
		if (null != this.defaultValue){
			YangStatement defaultValue = new YangStatement(YangStatement.YANG_STATEMENT_DEFAULT,
					this.getDefaultValue());
			typedef.addChild(defaultValue);
		}
		

	
		
		
		return typedef;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toXML()
	 */
	@Override
	public Element toXML() {
		
		
		Element typedef = new Element(XMLTag.XML_TAG_TYPEDEF);
		Element name = new Element(XMLTag.XML_TAG_NAME);
		name.setText(this.name);
		typedef.addContent(name);
		if (null != this.type){
			Element type = this.type.writeXML();
			if (null != type){
				typedef.addContent(type);
			}
			
		}
		
		
		if (null != this.units){
			Element units = new Element(XMLTag.XML_TAG_UNITS);
			units.setText(this.units);
			typedef.addContent(units);
		}
		
		if (null != this.defaultValue){
			Element defaultVal = new Element(XMLTag.XML_TAG_DEFAULT);
			defaultVal.setText(this.defaultValue);
			typedef.addContent(defaultVal);
		}
		
		if (null != this.description){
			Element descr = new Element(XMLTag.XML_TAG_DESCRIPTION);
			descr.setText(this.description);
			typedef.addContent(descr);
		}
		
		if (null != this.reference){
			Element ref = new Element(XMLTag.XML_TAG_REFERENCE);
			ref.setText(this.reference);
			typedef.addContent(ref);
			
		}

			
		if (!Status.CURRENT.equals(this.status)) {
			Element status = new Element(XMLTag.XML_TAG_STATUS);
			status.setText(this.status.getStatus());
			typedef.addContent(status);
		}

//		Element status = new Element(XMLTag.XML_TAG_STATUS);
//		status.setText(this.status.getStatus());
//		typedef.addContent(status);
		return typedef;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}
		

		if (element.getName().equals(XMLTag.XML_TAG_TYPE)){
			Env subEnv = newSubEnv();
			Type type =(Type) ModelElement.parseXML(subEnv, element);
			this.setType(type);
		}
		else if (element.getName().equals(XMLTag.XML_TAG_UNITS)){
			this.setUnits(element.getText());
		}
		else if (element.getName().equals(XMLTag.XML_TAG_DEFAULT)){
//			if (!this.checkDefaultValue(element.getText())){
//				return MODELELEMENT_FAIL;
//			}
			this.setDefaultValue(element.getText());
		}
		else if (element.getName().equals(XMLTag.XML_TAG_DESCRIPTION)){
			this.setDescription(element.getText());
		}
		else if (element.getName().equals(XMLTag.XML_TAG_STATUS)){
			this.setStatus(Status.getStatus(element.getText()));
		}
		else if (element.getName().equals(XMLTag.XML_TAG_REFERENCE)){
			this.setReference(element.getText());
		}
		else if (element.getName().equals(XMLTag.XML_TAG_NAME)){
			return MODELELEMENT_OK;
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}

	/**
	 * �жϴ�����Ĭ��ֵ����Ч��
	 * @param value
	 * @return
	 */
	private boolean checkDefaultValue(String value) {
		return this.getType().checkValue(value);
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYANG(com.netscenery.modeldesigner.util.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement statement) throws YangParserException, ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_TYPE)){
			Env subEnv = newSubEnv();
			Type type =(Type) ModelElement.parseYANG(subEnv, statement);
			this.setType(type);
		}
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_UNITS)){
			this.setUnits(statement.getArgument());
		}
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_DEFAULT)){
//			if (!this.checkDefaultValue(statement.getArgument())){
//				return MODELELEMENT_FAIL;
//			}
			this.setDefaultValue(statement.getArgument());
		}
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_DESCRIPTION)){
			this.setDescription(statement.getArgument());
		}
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_STATUS)){
			this.setStatus(Status.getStatus(statement.getArgument()));
		}
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_REFERENCE)){
			this.setReference(statement.getArgument());
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}

	public static Typedef getDefByQName(Env env,QName qname){
		Typedef def = null;
		if (null == qname){
			return null;
		}
		//�����QName�����ڱ��ص�typedefs��ȥ���Ƿ����
		//����Ƶ��һ�������string�Ķ���
		if (!qname.isQName()){
			def = env.getDerivedType(qname.getLocalName());
			//���û�ҵ�����ȥ��ģ����ģ����ȥ����
			if (null == def){
				ArrayList<SubModule> subModules = env.getSubModules();
				if (null != subModules){
					int size = subModules.size();
					for (int i = 0; i < size; i ++){
						SubModule sm = subModules.get(i);
						if (null != sm){
							def = sm.getTypedef(qname.getLocalName());
							if (null != def){
								break;
							}
						}
					}
				}
			}
		}
		else {
			//�ж��Ƿ����õ��Ǳ��ض���
			if((env.getCurModule() instanceof MainModule)
					&& (((MainModule) env.getCurModule()).getPrefix().equals(qname.getPrefix()))){
				
				def = env.getDerivedType(qname.getLocalName());
			
			}
			else if ((env.getCurModule() instanceof SubModule)
					&& (((SubModule) env.getCurModule()).getBelongsto().getPrefix().equals(qname.getPrefix()))){
				def = env.getDerivedType(qname.getLocalName());
			}
			else {
				/*
				 * ��������õ��ⲿ���壬����Ҫ����ģ��ĵ����б��в��Ҷ�Ӧprefix��ģ��
				 * �ٵ���ģ����ȥ����typedefs���Ƿ���ڸ�����һ�����Ϊstring�Ķ���
				 */
				Import imp = env.getCurModule().getImport(qname.getPrefix());
				if (null == imp){
					return null;
				}
				
				MainModule impModule = imp.getImportedModule();
				if (null == impModule){
					return null;
				}
				
				def = impModule.getTypedef(qname.getLocalName());
			}
			
		}
	
		return def;
	}

	public  MapInfo getMapInfo() throws SecurityException, NoSuchMethodException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Element toYin() {
		
		Element typedef = new Element(YangStatement.YANG_STATEMENT_TYPEDEF,env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME,this.name);
		typedef.setAttribute(name);
		if (!isNullString(this.getDescription())){
			Element descr = new Element(YangStatement.YANG_STATEMENT_DESCRIPTION,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(this.getDescription());
			descr.addContent(text);
			typedef.addContent(descr);
			
		}
		
		if (!isNullString(this.getReference())){
			Element ref = new Element(YangStatement.YANG_STATEMENT_REFERENCE,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(this.getReference());
			ref.addContent(text);
			typedef.addContent(ref);
		}
		
		if (Status.CURRENT != this.getStatus()){
			Element status = new Element(YangStatement.YANG_STATEMENT_STATUS,env.getNs());
			Attribute value = new Attribute(YangKeyword.VALUE.getKeyword(),this.getStatus().getStatus());
			status.setAttribute(value);
			typedef.addContent(status);
		}
		if (null != this.type){
			Element type = this.type.writeYin();
			if (null != type){
				typedef.addContent(type);
			}
			
		}
		
		
		if (null != this.units){
			Element units = new Element(YangStatement.YANG_STATEMENT_UNITS,env.getNs());
			Attribute uname = new  Attribute(XMLTag.XML_TAG_NAME
					,this.getUnits());
			units.setAttribute(uname);
			typedef.addContent(units);
		}
		
		if (null != this.defaultValue){
			Element dv = new Element(YangStatement.YANG_STATEMENT_DEFAULT,env.getNs());
			Attribute value = new Attribute(YangKeyword.VALUE.getKeyword(),
					this.getDefaultValue());		
			dv.setAttribute(value);
			typedef.addContent(dv);
		}
		


		
		
		return typedef;
	}

	@Override
	public int readYin(Element statement) throws ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		if (statement.getName().equals(YangStatement.YANG_STATEMENT_TYPE)){
			Env subEnv = newSubEnv();
			Type type =(Type) ModelElement.parseYin(subEnv, statement);
			this.setType(type);
		}
		else if (statement.getName().equals(YangStatement.YANG_STATEMENT_UNITS)){
			String value = statement.getAttributeValue(XMLTag.XML_TAG_NAME);
			this.setUnits(value);
		}
		else if (statement.getName().equals(YangStatement.YANG_STATEMENT_DEFAULT)){
//			if (!this.checkDefaultValue(statement.getArgument())){
//				return MODELELEMENT_FAIL;
//			}
			String value = statement.getAttributeValue(YangKeyword.VALUE.getKeyword());
			this.setDefaultValue(value);
		}
		else if (statement.getName().equals(YangStatement.YANG_STATEMENT_DESCRIPTION)){
			String value = statement.getChildText(XMLTag.XML_TAG_TEXT,env.getNs());
			this.setDescription(value);
		}
		else if (statement.getName().equals(YangStatement.YANG_STATEMENT_STATUS)){
			String value = statement.getAttributeValue(YangKeyword.VALUE.getKeyword());
			this.setStatus(Status.getStatus(value));
		}
		else if (statement.getName().equals(YangStatement.YANG_STATEMENT_REFERENCE)){
			String value = statement.getChildText(XMLTag.XML_TAG_TEXT,env.getNs());
			this.setReference(value);
		}
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		
	}


}
